﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.ComponentModel;

namespace Djs.Common.UI
{
    #region DblPanel : Třída určená ke kreslení grafiky na plochu. Jednodušší varianta.
    /// <summary>
    /// Třída určená ke kreslení grafiky na plochu - absolutně bez blikání.
    /// Jednodušší varianta: nabízí jednu standardní kreslící vrstvu.
    /// </summary>
    public class DblPanel : Panel, IDisposable
    {
		#region Konstrukce
        public DblPanel()
		{
			//this.Size = new Size(100, 50);
            this.ParentChanged += new EventHandler(DblPanel_ParentChanged);
            this.SetStyle(ControlStyles.SupportsTransparentBackColor | ControlStyles.ResizeRedraw |
                ControlStyles.AllPaintingInWmPaint | ControlStyles.UserMouse | ControlStyles.OptimizedDoubleBuffer, true);
            this.SetStyle(ControlStyles.Opaque, false);
			this._MainGraphBufferInit();
		}
        void DblPanel_ParentChanged(object sender, EventArgs e)
        {
            this.Draw();
        }
		void IDisposable.Dispose()
		{
            this._MainGraphBufferDispose();
            this.Dispose();
		}
		#endregion
        #region Kurzor
        /// <summary>
        /// Typ kurzoru. Vkládání hodnoty mění reálný kurzor, jen když je změna.
        /// </summary>
        public SysCursorType CursorType
        {
            get { return this._CursorType; }
            set
            {
                if (this._CursorType != value)
                {
                    this._CursorType = value;
                    this.Cursor = SysCursors.GetCursor(value);
                }
            }
        }
        private SysCursorType _CursorType = SysCursorType.Null;
        #endregion
        #region  KONSTRUKCE A ZAJIŠTĚNÍ ŘÍZENÍ BufferedGraphic : obecně přenosný mechanismus i do jiných tříd
        #region Privátní řídící mechanismus double-grafiky
        /// <summary>
        /// Řídící objekt bufferované grafiky
		/// </summary>
		private BufferedGraphicsContext _BuffGraphContent;
		/// <summary>
        /// Obsah bufferované grafiky, pro ryhlejší překreslování a udržení obrazu v paměti i mimo plochu Controlu
		/// </summary>
		private BufferedGraphics _BuffGraphics;
		/// <summary>
		/// Tato metoda do objektu this nastaví parametry pro doublebuffer grafiky.
		/// Tuto metodu voláme z konstruktoru objektu.
		/// </summary>
		private void _MainGraphBufferInit()
		{
			// Retrieves the BufferedGraphicsContext for the current application domain.
            this._BuffGraphContent = BufferedGraphicsManager.Current;

			// Sets the maximum size for the primary graphics buffer
			// of the buffered graphics context for the application
			// domain.  Any allocation requests for a buffer larger 
			// than this will create a temporary buffered graphics 
			// context to host the graphics buffer.
            this._BuffGraphContent.MaximumBuffer = new Size(this.Width + 1, this.Height + 1);

			// Allocates a graphics buffer the size of this form
			// using the pixel format of the Graphics created by 
			// the Form.CreateGraphics() method, which returns a 
			// Graphics object that matches the pixel format of the form.
            this._BuffGraphics = this._BuffGraphContent.Allocate(this.CreateGraphics(),
				 new Rectangle(0, 0, this.Width, this.Height));

			this.Resize += new EventHandler(Dbl_Resize);
			this.Paint += new PaintEventHandler(Dbl_Paint);
			this.SetStyle(ControlStyles.AllPaintingInWmPaint |
				ControlStyles.UserPaint |
				ControlStyles.SupportsTransparentBackColor |
				ControlStyles.OptimizedDoubleBuffer, true);

			// Draw the first frame to the buffer.
            this._Draw(Rectangle.Empty);
		}
        /// <summary>
        /// Dispose grafiky. Volá se z Dispose objektu, povinně !!!
        /// </summary>
        private void _MainGraphBufferDispose()
        {
            if (_BuffGraphics != null)
            {
                _BuffGraphics.Dispose();
                _BuffGraphics = null;
            }
            if (_BuffGraphContent != null)
            {
                _BuffGraphContent.Dispose();
                _BuffGraphContent = null;
            }
        }
        /// <summary>
		/// Fyzický Paint.
		/// Probíhá kdykoliv, když potřebuje okno překreslit.
		/// Aplikační logiku k tomu nepotřebuje, obrázek pro vykreslení má připravený v bufferu. Jen jej přesune na obrazovku.
		/// Aplikační logika kreslí v případě Resize (viz event Dbl_Resize) a v případě, kdy ona sama chce (když si vyvolá metodu Draw()).
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void Dbl_Paint(object sender, PaintEventArgs e)
		{
			// Okno chce vykreslit svoji grafiku - okamžitě je vylijeme do okna z našeho bufferu:
            this._BuffGraphics.Render(e.Graphics);
		}
		/// <summary>
		/// Handler události OnResize: zajistí přípravu nového bufferu, vyvolání kreslení do bufferu, a zobrazení dat z bufferu
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void Dbl_Resize(object sender, EventArgs e)
		{
			// Re-create the graphics buffer for a new window size.
			this._BuffGraphContent.MaximumBuffer = new Size(this.Width + 1, this.Height + 1);
            if (this._BuffGraphics != null)
			{
                this._BuffGraphics.Dispose();
                this._BuffGraphics = null;
			}
            if (this.Width > 0 && this.Height > 0)
            {
                this._BuffGraphics = this._BuffGraphContent.Allocate(this.CreateGraphics(),
                    new Rectangle(0, 0, this.Width, this.Height));

                // Cause the new drawing.
                this._Draw(Rectangle.Empty);
            }
		}
		/// <summary>
		/// Interní spouštěč metody pro kreslení dat
		/// </summary>
		private void _Draw(Rectangle drawRectangle)
		{
			if (this.Width > 0 && this.Height > 0 && this.Parent != null)
			{
				PaintEventArgs e = new PaintEventArgs(this._BuffGraphics.Graphics, drawRectangle /* new Rectangle(0, 0, this.Width, this.Height)*/ );
				this.OnPaintToBuffer(this, e);
				if (this.PaintToBuffer != null) this.PaintToBuffer(this, e);          // Event
			}
		}
		#endregion
		#region Public override property
		/// <summary>
		/// Pokud chceme využít bufferovaného vykreslování této třídy bez toho, abychom ji dědili (použijeme nativní třídu),
		/// pak je nutno vykreslování umístit do tohoto eventu.
		/// Pracuje se zde zcela stejně, jako v eventu Paint(), ale vizuální rozdíl je zcela zásadní:
		/// Zatímco Paint() kreslí přímo do controlu, naživo, a pokaždé znovu,
		/// pak tato metoda PaintToBuffer() kreslí do bufferu do paměti, a control si přebírá výsledek najednou, optimalizovaně.
		/// </summary>
		[Browsable(true)]
		[Category("Paint")]
		[Description("Zde musí být implementováno uživatelské vykreslování obsahu objektu do grafického bufferu. Pracuje se zde zcela stejně, jako v eventu Paint(), ale fyzicky se metoda volá jen v nutných případech.")]
		public event PaintEventHandler PaintToBuffer;
		/// <summary>
		/// Barva pozadí prvku. Je využita pokud není nastaveno (BackgroundIsTransparent == true)
		/// </summary>
		[Category("Appearance")]
		[Description("Barva pozadí prvku. Je využita pokud není nastaveno (BackgroundIsTransparent == true)")]
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
		public override Color BackColor
		{
			get { return base.BackColor; }
			set { base.BackColor = value; Draw(); }
		}
		#endregion
        #region Public řízení kreslení
        /// <summary>
		/// Metoda, která zajišťuje kreslení.
		/// Potomkové mohou využít, ale musí volat base(sender, e);
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		protected virtual void OnPaintToBuffer(object sender, PaintEventArgs e)
		{
			e.Graphics.Clear(this.BackColor);
		}
		/// <summary>
		/// Tato metoda zajistí nové vykreslení objektu. Používá se namísto Invalidate() !!!
		/// Důvodem je to, že Invalidate() znovu vykreslí obsah bufferu - ale ten obsahuje "stará" data.
		/// Vyvolá událost PaintToBuffer() a pak přenese vykreslený obsah z bufferu do vizuálního controlu.
		/// </summary>
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
		public void Draw()
		{
			this._Draw(Rectangle.Empty);
		}
        /// <summary>
        /// Tato metoda zajistí nové vykreslení objektu. Používá se namísto Invalidate() !!!
        /// Důvodem je to, že Invalidate() znovu vykreslí obsah bufferu - ale ten obsahuje "stará" data.
        /// Vyvolá událost PaintToBuffer() a pak přenese vykreslený obsah z bufferu do vizuálního controlu.
        /// </summary>
        /// <param name="renderToControl">true = Provést renderování do Controlu</param>
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public void Draw(bool renderToControl)
        {
            this._Draw(Rectangle.Empty);
            if (renderToControl)
                this._BuffGraphics.Render(this.CreateGraphics());
        }
        /// <summary>
		/// Refresh musí zajistit kompletní RePaint do bufferu
		/// </summary>
		public override void Refresh()
		{
            if (this.InvokeRequired)
            {
                this.BeginInvoke(new Action(this.Refresh));
            }
            else
            {
                this._Draw(Rectangle.Empty);
                base.Refresh();
            }
		}
		#endregion
		#endregion
    }
    #endregion
    #region LayeredPanel : Třída určená ke kreslení grafiky na plochu. Varianta pro složitější interaktivní motivy.
    /// <summary>
    /// Třída určená ke kreslení grafiky na plochu - absolutně bez blikání.
    /// Varianta pro složitější interaktivní motivy: nabízí více vrstev pro kreslení.
    /// </summary>
    public class LayeredPanel : Panel, IDisposable
    {
        #region Konstrukce, proměnné, vnitřní eventy
        public LayeredPanel()
        {
            this._LayerList = new List<GraphicLayer>();
            this.LayerCount = 1;
            this.Resize += new EventHandler(_Resize);
            this.Paint += new PaintEventHandler(_Paint);
            this.SetStyle(ControlStyles.AllPaintingInWmPaint |
                ControlStyles.UserPaint |
                ControlStyles.SupportsTransparentBackColor |
                ControlStyles.OptimizedDoubleBuffer, true);
            this.ResizeRedraw = true;
        }
        /// <summary>
        /// Nativní událost okna při změně velikosti.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void _Resize(object sender, EventArgs e)
        {
            this._ResizeLayers();
        }
        /// <summary>
        /// Nativní událost okna, když chce překreslit svůj obsah.
        /// Obsah se vezme z bufferovaných vrstev a vykreslí do předané grafiky.
        /// Není k tomu vyvolaná aplikační logika.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void _Paint(object sender, PaintEventArgs e)
        {
            if (this._ReDrawNeed)
                this.ReDraw();
            this._RenderValidLayerTo(e.Graphics);
        }
        public override void Refresh()
        {
            this.ReDraw();
            base.Refresh();
        }
        #endregion
        #region Public property a metody a eventy
        /// <summary>
        /// Počet vrstev, v rozmezí 1 - 10.
        /// Změna hodnoty vyvolá ReDraw().
        /// </summary>
        public int LayerCount
        {
            get { return this._LayerCount; }
            set
            {
                int cnt = value;
                if (cnt < 1) cnt = 1;
                if (cnt > 10) cnt = 10;
                if (cnt == this._LayerCount) return;
                this._LayerCount = cnt;
                this._CreateLayers();
                this.ReDraw();
            }
        }
        /// <summary>
        /// Tato metoda se volá tehdy, když aplikace chce překreslit celý objekt.
        /// </summary>
        public void ReDraw()
        {
            this._ReDraw(null, null);
        }
        /// <summary>
        /// Tato metoda se volá tehdy, když aplikace chce překreslit celý objekt.
        /// </summary>
        public void ReDraw(params int[] layers)
        {
            this._ReDraw(null, layers);
        }
        /// <summary>
        /// Tato metoda se volá tehdy, když aplikace chce překreslit celý objekt.
        /// </summary>
        public void ReDraw(object userData, IEnumerable<int> drawLayers)
        {
            this._ReDraw(userData, drawLayers);
        }
        /// <summary>
        /// Událost, kdy se má překreslit obsah vrstev controlu.
        /// Pokud někdo chce použít tuto třídu bez jejího dědění, pak může svoje kreslení provést v eventhandleru této události.
        /// </summary>
        public event LayeredPaintEventHandler PaintLayers;
        #endregion
        #region Řízení kreslení: private _ReDraw(); protected OnPaintLayers(); 
        /// <summary>
        /// Zajistí překreslení daných vrstev objektu
        /// </summary>
        /// <param name="userData"></param>
        /// <param name="drawLayers"></param>
        private void _ReDraw(object userData, IEnumerable<int> drawLayers)
        {
            if (this.Width > 0 && this.Height > 0 && this.Parent != null)
            {
                LayeredPaintEventArgs e = new LayeredPaintEventArgs(this._LayerCount, this._GetGraphicsForLayer, this._CopyContentOfLayer, drawLayers, userData);
                this.OnPaintLayers(e);
                this._ValidLayer = e.ValidLayer;
                this._ReDrawNeed = false;
                this._RenderValidLayerTo(this.CreateGraphics());
            }
            else
            {
                this._ReDrawNeed = true;
            }
        }
        /// <summary>
        /// Příznak, že poslední běh metody this._ReDraw() byl vynechán
        /// </summary>
        private bool _ReDrawNeed;
        /// <summary>
        /// Číslo Layeru, který obsahuje validní informaci, která se bude renderovat do controlu.
        /// Určuje ji argument LayeredPaintEventArgs spolu s metodami OnPaintLayers().
        /// </summary>
        private int _ValidLayer;
        /// <summary>
        /// Háček, který se volá při potřebě překreslit obsah objektu.
        /// Typicky jej overriduje potomek a provádí kreslení.
        /// Bázová metoda provádí pouze Clear() vrstvy 0, a to jen tehdy pokud se má kreslit. Clear() vloží do vrstvy 0 barvu this.BackColor.
        /// Dále volá event PaintLayers.
        /// </summary>
        /// <param name="e"></param>
        protected virtual void OnPaintLayers(LayeredPaintEventArgs e)
        {
            // Clear layer 0:
            if (e.HasPaintLayer(0))
                e.GraphicsForLayer(0).Clear(this.BackColor);

            // event:
            if (this.PaintLayers != null)
                this.PaintLayers(this, e);
        }
        /// <summary>
        /// Vrátí objekt Graphics pro danou vrstvu layer.
        /// </summary>
        /// <param name="layer"></param>
        /// <returns></returns>
        private Graphics _GetGraphicsForLayer(int layer)
        {
            if (this._LayerList != null && layer >= 0 && layer < this._LayerList.Count)
                return this._LayerList[layer].LayerGraphics;
            return null;
        }
        /// <summary>
        /// Kopíruje obsah vrstvy (layerFrom) to vrstvy (layerTo)
        /// </summary>
        /// <param name="layerFrom"></param>
        /// <param name="layerTo"></param>
        private void _CopyContentOfLayer(int layerFrom, int layerTo)
        {
            GraphicLayer layerSource = this._LayerList[layerFrom];
            Graphics graphicsTarget = this._LayerList[layerTo].LayerGraphics;
            layerSource.RenderTo(graphicsTarget);
        }
        /// <summary>
        /// Fyzicky překreslí obsah bufferů do předané grafiky.
        /// </summary>
        /// <param name="graphics"></param>
        private void _RenderValidLayerTo(Graphics graphics)
        {
            int validLayer = this._ValidLayer;
            if (validLayer >= 0 && validLayer < this._LayerCount)
                this._LayerList[validLayer].RenderTo(graphics);
        }
        #endregion
        #region Řízení vrstev: Create, Resize, Dispose. Nikoliv kreslení.
        private void _CreateLayers()
        {
            Size size = this.Size;
            this._LayerList = new List<GraphicLayer>();
            for (int l = 0; l < this._LayerCount; l++)
                this._LayerList.Add(new GraphicLayer(this, this.Size));
        }
        private void _ResizeLayers()
        {
            this._CreateLayers();
            this.ReDraw();
        }
        void IDisposable.Dispose()
        {
            if (this._LayerList != null)
            {
                foreach (GraphicLayer layer in this._LayerList)
                    ((IDisposable)layer).Dispose();
            }
            this._LayerList = null;
            this._LayerCount = 0;
        }
        private int _LayerCount;
        private List<GraphicLayer> _LayerList;
        #endregion
        #region class GraphicLayer : třída představující jednu grafickou vrstvu
        /// <summary>
        /// GraphicLayer : třída představující jednu grafickou vrstvu
        /// </summary>
        protected class GraphicLayer : IDisposable
        {
            #region Konstrukce a proměnné
            /// <summary>
            /// Konstruktor vrstvy. Potřebuje referenci na objekt, který jej bude vlastnit, kvůli získávání jeho Graphics.
            /// </summary>
            /// <param name="owner"></param>
            public GraphicLayer(Control owner)
            {
                this._Owner = owner;
            }
            /// <summary>
            /// Konstruktor vrstvy. Potřebuje referenci na objekt, který jej bude vlastnit, kvůli získávání jeho Graphics.
            /// </summary>
            /// <param name="owner"></param>
            public GraphicLayer(Control owner, Size size)
            {
                this._Owner = owner;
                this.Size = size;
            }
            private Control _Owner;
            /// <summary>
            /// Řídící objekt bufferované grafiky
            /// </summary>
            private BufferedGraphicsContext _BuffGraphContext;
            /// <summary>
            /// Obsah bufferované grafiky, pro rychlejší překreslování a udržení obrazu v paměti i mimo plochu Controlu
            /// </summary>
            private BufferedGraphics _BuffGraphicsData;
            /// <summary>
            /// Velikost
            /// </summary>
            private Size _Size;
            #endregion
            #region Public property a metody
            /// <summary>
            /// true pokud je objekt platný
            /// </summary>
            public bool IsValid
            {
                get { return (!this._Size.IsEmpty && this._BuffGraphContext != null && this._BuffGraphicsData != null); }
            }
            /// <summary>
            /// Rozměr vrstvy.
            /// Lze nastavit, vrstva se upraví. Poté je třeba překreslit (vrstva sama si nevolá).
            /// </summary>
            public Size Size
            {
                get { return this._Size; }
                set
                {
                    if (this.IsValid && value == this._Size) return;         // Není důvod ke změně
                    this._Size = value;
                    this.CreateLayer();
                }
            }
            /// <summary>
            /// Objekt Graphics, který dovoluje kreslit motivy do této vrstvy
            /// </summary>
            public Graphics LayerGraphics { get { return this._BuffGraphicsData.Graphics; } }
            /// <summary>
            /// Vykreslí svůj obsah do dané cílové Graphics, typicky při kreslení Controlu (skládají se jednotlivé vrstvy).
            /// </summary>
            /// <param name="targetGraphic"></param>
            public void RenderTo(Graphics targetGraphic)
            {
                targetGraphic.CompositingMode = CompositingMode.SourceOver;
                this._BuffGraphicsData.Graphics.CompositingMode = CompositingMode.SourceOver;
                this._BuffGraphicsData.Render(targetGraphic);
            }
            #endregion
            #region Privátní tvorba grafiky, IDisposable
            protected void CreateLayer()
            {
                if (this._BuffGraphContext == null)
                    this._BuffGraphContext = BufferedGraphicsManager.Current;

                Size s = this._Size;
                if (s.Width <= 0) s.Width = 1;
                if (s.Height <= 0) s.Height = 1;

                this._BuffGraphContext.MaximumBuffer = new Size(s.Width + 1, s.Height + 1);

                if (this._BuffGraphicsData != null)
                    this._BuffGraphicsData.Dispose();

                this._BuffGraphicsData = this._BuffGraphContext.Allocate(this._Owner.CreateGraphics(), new Rectangle(new Point(0, 0), s));
            }
            void IDisposable.Dispose()
            {
                if (this._BuffGraphContext != null)
                    this._BuffGraphContext.Dispose();
                this._BuffGraphContext = null;

                if (this._BuffGraphicsData != null)
                    this._BuffGraphicsData.Dispose();
                this._BuffGraphicsData = null;

                this._Owner = null;
            }
            #endregion
        }
        #endregion
    }
    #region Delegát LayeredPaintEventHandler, EventArgs LayeredPaintEventArgs
    /// <summary>
    /// Předpis pro handler události LayeredPaint
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    public delegate void LayeredPaintEventHandler(object sender, LayeredPaintEventArgs e);
    /// <summary>
    /// Data pro metodu OnLayeredPaint() / event LayeredPaint
    /// </summary>
    public class LayeredPaintEventArgs : EventArgs
    {
        public LayeredPaintEventArgs(int layerCount, Func<int, Graphics> getGraphics, Action<int, int> copyContentOfLayer, IEnumerable<int> layersToPaint, object userData)
        {
            this._LayerCount = layerCount;
            this._GetGraphics = getGraphics;
            this._CopyContentOfLayer = copyContentOfLayer;
           
            this._LayersToPaint = new Dictionary<int,object>();
            if (layersToPaint != null)
            {
                foreach (int layer in layersToPaint)
                {
                    if (this.LayerExists(layer) && !this._LayersToPaint.ContainsKey(layer))
                        this._LayersToPaint.Add(layer, null);
                }
            }
            else
            {
                for (int layer = 0; layer < layerCount; layer++)
                    this._LayersToPaint.Add(layer, null);
            }

            this.UserData = userData;
        }
        /// <summary>Počet vrstev, slouží ke kontrole čísla vrstvy</summary>
        private int _LayerCount;
        /// <summary>Funkce, která vrátí Graphics pro danou vrstvu</summary>
        private Func<int, Graphics> _GetGraphics;
        /// <summary>Metoda, která překopíruje obsah vrstvy (1) do vrstvy (2)</summary>
        private Action<int, int> _CopyContentOfLayer;
        /// <summary>Soupis vrstev, které se mají kreslit</summary>
        private Dictionary<int, object> _LayersToPaint;
        /// <summary>
        /// Vrací true, pokud dané číslo vrstvy existuje (a může tedy vrátit Graphics)
        /// </summary>
        /// <param name="layer"></param>
        /// <returns></returns>
        public bool LayerExists(int layer)
        {
            if (layer < 0 || layer >= this._LayerCount) return false;
            return true;
        }
        /// <summary>
        /// Vrátí true, pokud se má kreslit do dané vrstvy.
        /// </summary>
        /// <param name="layer"></param>
        /// <returns></returns>
        public bool HasPaintLayer(int layer)
        {
            if (!this.LayerExists(layer)) return false;
            if (this._LayersToPaint == null) return true;
            return this._LayersToPaint.ContainsKey(layer);
        }
        /// <summary>
        /// Soupis vrstev, které se mají kreslit.
        /// </summary>
        public IEnumerable<int> LayersToPaint { get { return this._LayersToPaint.Keys; } }
        /// <summary>
        /// Vrátí objekt Graphics pro danou vrstvu.
        /// Vrací objekt i pro vrstvy, do nichž se kreslit nemusí.
        /// Pro neexistující vrstvy vrací null.
        /// </summary>
        /// <param name="layer"></param>
        /// <returns></returns>
        public Graphics GraphicsForLayer(int layer)
        {
            this.ValidLayer = layer;
            return this._GetGraphics(layer);
        }
        /// <summary>
        /// Zkopíruje obsah vrstvy (layerFrom) do vrstvy (layerTo).
        /// Používá se při kreslení jen části motivu, kdy se jako podklad přebírá již dříve připravený obsah.
        /// </summary>
        /// <param name="layerFrom"></param>
        /// <param name="layerTo"></param>
        public void CopyContentOfLayer(int layerFrom, int layerTo)
        {
            this._CopyContentOfLayer(layerFrom, layerTo);
            this.ValidLayer = layerTo;
        }
        /// <summary>
        /// Libovolná data, předaná do metody ReDraw.
        /// </summary>
        public object UserData { get; private set; }
        /// <summary>
        /// Index vrstvy, která obsahuje validní data.
        /// Automaticky se zde udržuje index vrstvy, která byla posledním cílem operace CopyContentOfLayer(), anebo která byla naposledy vyzvednuta ke kreslení metodou GraphicsForLayer().
        /// Nicméně aplikace může na konci metody override OnPaintLayers() vložit do argumentu do property ValidLayer libovolnou vrstvu, která se bude používat jako zdroj obrazu pro vykreslení controlu.
        /// </summary>
        public int ValidLayer { get; set; }
    }
    #endregion
    #endregion
    #region class SysCursors, enum SysCursorType
    /// <summary>
    /// Práce s objekty Cursor
    /// </summary>
    internal class SysCursors
    {
        public static Cursor GetCursor(SysCursorType cursorType)
        {
            switch (cursorType)
            {
                case SysCursorType.AppStarting: return Cursors.AppStarting;
                case SysCursorType.Arrow: return Cursors.Arrow;
                case SysCursorType.Cross: return Cursors.Cross;
                case SysCursorType.Default: return Cursors.Default;
                case SysCursorType.Hand: return Cursors.Hand;
                case SysCursorType.Help: return Cursors.Help;
                case SysCursorType.HSplit: return Cursors.HSplit;
                case SysCursorType.IBeam: return Cursors.IBeam;
                case SysCursorType.No: return Cursors.No;
                case SysCursorType.NoMove2D: return Cursors.NoMove2D;
                case SysCursorType.NoMoveHoriz: return Cursors.NoMoveHoriz;
                case SysCursorType.NoMoveVert: return Cursors.NoMoveVert;
                case SysCursorType.PanEast: return Cursors.PanEast;
                case SysCursorType.PanNE: return Cursors.PanNE;
                case SysCursorType.PanNorth: return Cursors.PanNorth;
                case SysCursorType.PanNW: return Cursors.PanNW;
                case SysCursorType.PanSE: return Cursors.PanSE;
                case SysCursorType.PanSouth: return Cursors.PanSouth;
                case SysCursorType.PanSW: return Cursors.PanSW;
                case SysCursorType.PanWest: return Cursors.PanWest;
                case SysCursorType.SizeAll: return Cursors.SizeAll;
                case SysCursorType.SizeNESW: return Cursors.SizeNESW;
                case SysCursorType.SizeNS: return Cursors.SizeNS;
                case SysCursorType.SizeNWSE: return Cursors.SizeNWSE;
                case SysCursorType.SizeWE: return Cursors.SizeWE;
                case SysCursorType.UpArrow: return Cursors.UpArrow;
                case SysCursorType.VSplit: return Cursors.VSplit;
                case SysCursorType.WaitCursor: return Cursors.WaitCursor;

                // case SysCursorType.ExtCrossDoc: return Pics.IconLibraryMin.Cross_r_doc;
            }
            return Cursors.Default;
        }
    }
    /// <summary>
    /// Druh kurzoru.
    /// Větší část hodnot odpovídá systémovým kurzorům třídy System.Windows.Forms.Cursors
    /// Konkrétní objekt Cursor lze získat statickou metodou třídy SysCursors.GetCursor(SysCursorType cursorType)
    /// </summary>
    public enum SysCursorType
    {
        /// <summary>No change (žádná změna)</summary>
        Null = 0,
        /// <summary>cursor that appears when an application starts.</summary>
        AppStarting,
        /// <summary>arrow cursor.</summary>
        Arrow,
        /// <summary>crosshair cursor.</summary>
        Cross,
        /// <summary>default cursor, which is usually an arrow cursor.</summary>
        Default,
        /// <summary>hand cursor, typically used when hovering over a Web link.</summary>
        Hand,
        /// <summary>Help cursor, which is a combination of an arrow and a question mark.</summary>
        Help,
        /// <summary>cursor that appears when the mouse is positioned over a horizontal splitter bar.</summary>
        HSplit,
        /// <summary>I-beam cursor, which is used to show where the text cursor appears when the mouse is clicked.</summary>
        IBeam,
        /// <summary>cursor that indicates that a particular region is invalid for the current operation.</summary>
        No,
        /// <summary>cursor that appears during wheel operations when the mouse is not moving, but the window can be scrolled in both a horizontal and vertical direction.</summary>
        NoMove2D,
        /// <summary>cursor that appears during wheel operations when the mouse is not moving, but the window can be scrolled in a horizontal direction.</summary>
        NoMoveHoriz,
        /// <summary>cursor that appears during wheel operations when the mouse is not moving, but the window can be scrolled in a vertical direction.</summary>
        NoMoveVert,
        /// <summary>cursor that appears during wheel operations when the mouse is moving and the window is scrolling horizontally to the right.</summary>
        PanEast,
        /// <summary>cursor that appears during wheel operations when the mouse is moving and the window is scrolling horizontally and vertically upward and to the right.</summary>
        PanNE,
        /// <summary>cursor that appears during wheel operations when the mouse is moving and the window is scrolling vertically in an upward direction.</summary>
        PanNorth,
        /// <summary>cursor that appears during wheel operations when the mouse is moving and the window is scrolling horizontally and vertically upward and to the left.</summary>
        PanNW,
        /// <summary>cursor that appears during wheel operations when the mouse is moving and the window is scrolling horizontally and vertically downward and to the right.</summary>
        PanSE,
        /// <summary>cursor that appears during wheel operations when the mouse is moving and the window is scrolling vertically in a downward direction.</summary>
        PanSouth,
        /// <summary>cursor that appears during wheel operations when the mouse is moving and the window is scrolling horizontally and vertically downward and to the left.</summary>
        PanSW,
        /// <summary>cursor that appears during wheel operations when the mouse is moving and the window is scrolling horizontally to the left.</summary>
        PanWest,
        /// <summary>four-headed sizing cursor, which consists of four joined arrows that point north, south, east, and west.</summary>
        SizeAll,
        /// <summary>two-headed diagonal (northeast/southwest) sizing cursor.</summary>
        SizeNESW,
        /// <summary>two-headed vertical (north/south) sizing cursor.</summary>
        SizeNS,
        /// <summary>two-headed diagonal (northwest/southeast) sizing cursor.</summary>
        SizeNWSE,
        /// <summary>two-headed horizontal (west/east) sizing cursor.</summary>
        SizeWE,
        /// <summary>up arrow cursor, typically used to identify an insertion point.</summary>
        UpArrow,
        /// <summary>cursor that appears when the mouse is positioned over a vertical splitter bar.</summary>
        VSplit,
        /// <summary>wait cursor, typically an hourglass shape.</summary>
        WaitCursor,
        /// <summary>Ne-systémový kurzor: kříž s obdélníkem vpravo dole, pro insert objektu.</summary>
        ExtCrossDoc,
        /// <summary>
        /// Nejde o standardní kurzor, ale o požadavek na kurzor typu NoMoveHoriz nebo NoMoveVert nebo NoMove2D, podle aktuálního typu přesunu.
        /// </summary>
        NoMoveAuto
    }
    #endregion
}
